// class
// new

import 'package:dio/dio.dart';
import 'package:flutter_enjoy_plus/constants/index.dart';
import 'package:flutter_enjoy_plus/utils/Emitter.dart';
import 'package:flutter_enjoy_plus/utils/TokenManager.dart';
import 'package:flutter_enjoy_plus/utils/toast.dart';

class RequestDio {
  final Dio _dio = Dio();
  RequestDio() {
    // 针对dio的基础地址进行设置
    // _dio.options.baseUrl = GlobalVariable.BASE_URL; // 设置基础地址
    // _dio.options.receiveTimeout =
    //     const Duration(seconds: GlobalVariable.TIME_OUT); // 接收时间
    // _dio.options.sendTimeout =
    //     const Duration(seconds: GlobalVariable.TIME_OUT); // 发送超时时间

    // 写法2：想要针对一个对象连续的进行赋值可以用..的方式
    _dio
    ..options.baseUrl = GlobalVariable.BASE_URL // 设置基础地址
    ..options.connectTimeout  =  const Duration(seconds: GlobalVariable.TIME_OUT) // 接收时间
    ..options.receiveTimeout  =  const Duration(seconds: GlobalVariable.TIME_OUT);

    // axios 和 rcp的拦截器
    // 添加请求和响应拦截器
    _registerInterceptors(); // 注册拦截器
  }

  // get 
  Future<dynamic> get(String url, {Map<String, dynamic>? params}) async {
    final result = await _dio.get(url, queryParameters: params);
    return _handleRequest(result);
  }
  // post
  Future<dynamic> post(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.post(url, data: data);
    return _handleRequest(result);
  }

  // delete方法
  Future<dynamic> delete(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.delete(url, data: data);
    return _handleRequest(result);
  }

  // put方法
  Future<dynamic> put(String url, {Map<String, dynamic>? data}) async {
    final result = await _dio.put(url, data: data);
    return _handleRequest(result);
  }

  // upload上传图片方法 (相当于另一种需求的post)
  // 原因：虽然也是post请求，但是上传参数为form-data格式,上述post方法无法满足上传图片
  Future upload(String url, {FormData? data}) async {
    final result = await _dio.post(url, data: data);
    return _handleRequest(result);
  }

  // 针对所有请求 - 统一处理返回的结果
  _handleRequest(Response<dynamic> response){
    if(response.data['code'] == GlobalVariable.SUCCESS_CODE){
      // 请求业务成功
      return response.data['data'];
    }
    // 请求业务失败
    else{
      // 抛出异常（程序员）
      throw Exception(response.data['message']);
      // 增加提示（用户）
    }
  }

  // 判断token是否过期
  Future<bool> _isRefreshToken() async{
    final refreshToken = tokenmanager.getRefreshToken();
    Dio dio = Dio();
    try {
      final res = await dio.post(
        '${GlobalVariable.BASE_URL}${HTTP_PATH.REFRESH_TOKEN}',
        options: Options(headers: {'Authorization': 'Bearer $refreshToken'})
      );
      if(res.data['code'] == 10000){
        // 刷新token成功
        // 将获取到的最新token和refreshToken进行存储
        tokenmanager.setToken(res.data['data']['token'],refresh_token: res.data['data']['refresh_token']);
        return true;
      }
      else{
        return false;
      }
    } catch (e) {
      return false;
    }
  }



  // 拦截器
  _registerInterceptors() {
    _dio.interceptors.add(
      InterceptorsWrapper(
        // 请求拦截
        onRequest: (RequestOptions options, RequestInterceptorHandler handler) {
          // 如果你想完成请求并返回一些自定义数据，你可以使用 `handler.resolve(response)`。
          // 如果你想终止请求并触发一个错误，你可以使用 `handler.reject(error)`。

          // 注入token
          final token = tokenmanager.getToken(); 
          if(token.isNotEmpty){
            options.headers['Authorization'] = 'Bearer $token';
          }
          return handler.next(options);
        },
        // 响应拦截
        onResponse: (Response response, ResponseInterceptorHandler handler) {
          // 如果你想终止请求并触发一个错误，你可以使用 `handler.reject(error)`。
          // 针对于http状态码 大于等于200 && 小于300的情况（请求成功）
          if(response.statusCode! >= 200 && response.statusCode! < 300){
            return handler.next(response);
          }else{
            // 4开头 （客户端异常 -> 请求路径 错误/ 鉴权）
            PrompAction.showFail(response.statusMessage!);
            // 
            return handler.reject(
              DioException(
                requestOptions: response.requestOptions,
                type: DioExceptionType.badResponse,
                error: response.statusMessage,
              )
            );
          }
          
        },
        // 异常处理
        onError: (DioException error, ErrorInterceptorHandler handler) async {
          // 如果你想完成请求并返回一些自定义数据，你可以使用 `handler.resolve(response)`。
          // 401 授权失败的逻辑
          if (error.response?.statusCode == 401){
            // 一.不考虑刷新token
            // // 清除本地缓存
            // // 跳转到登录
            // tokenmanager.deleteToken();
            // // 非页面级组件无法使用context上下文对象
            // // Navigator.pushNamed(context,'/login')
            // // eventBus 事件总线 -> vue中
            // // 发布订阅模式 -> 发布者 / 订阅者
            // // 鸿蒙中emitter / 前端 eventBus

            // // 1.发布一个消息
            // eventBus.fire(LogoutEvent());
            // // 2.其他可以跳转的位置进行订阅 -> 跳转

            // 二.考虑刷新token -> 
            // 1.获取refreshToken
            // 2.判断refreshToken是否存在
            // 2.2 如果存在 继续判断refreshToken是否过期
            // 2.3 如何判定refreshToken是否过期 -> 取决于 拿着刷新token 请求接口时，判断返回状态码

            final refreshToken = tokenmanager.getRefreshToken();
            if(refreshToken.isNotEmpty){
              // 刷新token存在情况
              // 是否过期 封装方法 返回布尔值 没有过期 / 返回false 过期
              bool isOk = await _isRefreshToken();
              if(isOk){
                // refreshToken有效 -> 存储新token 和 新refreshToken 成功了
                // 一旦refreshToken有效, 存储了最新的token和最新的refreshToken
                // 上述步骤, 从数据层面, 但是视图层面, 需要及时更新
                // 我的页面, 刚才是请求401 -> 拿着最新的token, 重新再次发送请求 -> 更新我的页面用户数据
                //  TODO 
                // 尝试发起之前失败的请求
                // error.requestOptions (上次请求的参数对象)
                // 再次发起请求, 拿成功的结果 dio.fetch(error.requestOptions)
                final res = await _dio.fetch(error.requestOptions);
                return handler.resolve(res);
              }
              else{
                // 清除本地缓存
                // 跳转到登录页
                tokenmanager.deleteToken();
                eventBus.fire(LoginEvent());
              }

            }
            else{
              // 刷新token不存在情况
              // 清除本地缓存
              // 跳转到登录页
              tokenmanager.deleteToken();
              eventBus.fire(LogoutEvent());
            }
          }
          return handler.next(error);
        },
      ),
    );
  }
}

final requestDio = RequestDio();
